package cn.jilian.dao.support;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.engine.jdbc.internal.FormatStyle;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SessionEventListenerManager;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerationException;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jdbc.AbstractReturningWork;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.type.Type;

public class FuncSequenceGenerator
	implements IdentifierGenerator, Configurable {

public static final String SEQ_NAME="seq_name";

public static final String DEFAULT_SEQ_NAME="seq_sid";

public static final String SEQ_FUNC="seq_func";

public static final String DEFAULT_SEQ_FUNC="nextval";

private String seq_name;

private String seq_func;

private String sql;




@Override
public void configure(Type type, Properties params, ServiceRegistry serviceRegistry) throws MappingException {
	
	this.seq_name = ConfigurationHelper.getString( SEQ_NAME, params, DEFAULT_SEQ_NAME );
	this.seq_func=ConfigurationHelper.getString( SEQ_FUNC, params, DEFAULT_SEQ_FUNC);
	this.sql=new StringBuilder("select ").append(seq_func)
			.append("('").append(seq_name).append("') as id").toString();
}

protected Long generateHolder(final SharedSessionContractImplementor session) {
	final SqlStatementLogger statementLogger = session.getFactory().getServiceRegistry()
			.getService( JdbcServices.class )
			.getSqlStatementLogger();
	final SessionEventListenerManager listeners = session.getEventListenerManager();

	return session.getTransactionCoordinator().createIsolationDelegate().delegateWork(
			new AbstractReturningWork<Long>() {
				@Override
				public Long execute(Connection connection) throws SQLException {
					final PreparedStatement qps = prepareStatement( connection,FuncSequenceGenerator. this.sql, statementLogger, listeners );
					Long value=0L;
					try {
						ResultSet rs = executeQuery( qps, listeners );
						if ( !rs.next() ) {
							String err = "could not read a hi value - you need to populate the func: " + seq_func;
						
							throw new IdentifierGenerationException(err);
						}else {
							value= rs.getLong("id");
						}
						rs.close();
					}
					catch (SQLException e) {
						throw e;
					}
					finally {
						qps.close();
					}
					return value;
				}
			},
			true
	);
}

private PreparedStatement prepareStatement(
		Connection connection,
		String sql,
		SqlStatementLogger statementLogger,
		SessionEventListenerManager statsCollector) throws SQLException {
	statementLogger.logStatement( sql, FormatStyle.BASIC.getFormatter() );
	try {
		statsCollector.jdbcPrepareStatementStart();
		return connection.prepareStatement( sql );
	}
	finally {
		statsCollector.jdbcPrepareStatementEnd();
	}
}


private ResultSet executeQuery(PreparedStatement ps, SessionEventListenerManager statsCollector) throws SQLException {
	try {
		statsCollector.jdbcExecuteStatementStart();
		return ps.executeQuery();
	}
	finally {
		statsCollector.jdbcExecuteStatementEnd();
	}
}

@Override
public Serializable generate(SharedSessionContractImplementor session, Object object) throws HibernateException {
	return generateHolder(session);
}

	

}
